#pragma once

#include "action.h"
#include "templatevector.h"
#include "bitmap.h"

class Action_ChangePixel : public Action
{
protected:
	struct PixelChanged
	{
		/** what pixel was changed */
		SPoint m_pos;
		/** old value */
		int m_oldValue;
		/** new value */
		int m_newValue;
		/** default constructor */
		PixelChanged(){}
		/** complete constructor */
		PixelChanged(const SPoint & a_pos, const int & a_oldValue, const int & a_newValue)
			:m_pos(a_pos),m_oldValue(a_oldValue),m_newValue(a_newValue){}
		inline void redo(BitMaP * a_bmp){a_bmp->setPixelAt(m_pos, m_newValue);}
		inline void undo(BitMaP * a_bmp){a_bmp->setPixelAt(m_pos, m_oldValue);}
		inline bool isChange(){return m_oldValue != m_newValue;}
		bool equals(const PixelChanged & a_pc)
		{
			return a_pc.m_pos.equals(m_pos)
				&& a_pc.m_oldValue == m_oldValue
				&& a_pc.m_newValue == m_newValue;
		}
		bool is(const SPoint & a_p, const int & a_newValue)
		{
			return m_pos.equals(a_p)
				&& m_newValue == a_newValue;
		}
	};
	/** the image where pixels have beed changed */
	BitMaP * m_bmp;
	/** which pixels have been changed */
	TemplateVector<PixelChanged> m_pixels;
	/** an arbitrary indicator of whether or not this action can have sub actions added to it */
	bool m_canBeAddedTo;

public:
	/** the static name of this action type */
	static const char * GETNAME(){return "ChangePixel";}
	/** what to call this action */
	virtual const char * getName()const{return GETNAME();}

	void setCanBeAddedTo(const bool & a_canBeAddedTo){m_canBeAddedTo = a_canBeAddedTo;}

	/** add to an existing ChangePixel action*/
	void addAction_ChangePixel(const SPoint & a_pos, int a_newValue)
	{
		// don't add a change that we have already...
		bool existsAlready = false;
		for(int i = 0; !existsAlready && i < m_pixels.size(); ++i)
		{
			existsAlready |= m_pixels.get(i).is(a_pos, a_newValue);
		}
		if(!existsAlready)
		{
			// only add changes that actually change the image...
			PixelChanged change(a_pos, m_bmp->getPixelAt(a_pos), a_newValue);
			if(change.isChange())
			{
				m_pixels.add(change);
			}
		}
	}
	/** make a new ChangePixel action*/
	Action_ChangePixel(BitMaP * a_bmp, const SPoint & a_pos, const int & a_newValue)
		:m_bmp(a_bmp),m_canBeAddedTo(true)
	{addAction_ChangePixel(a_pos, a_newValue);}

	Action_ChangePixel(const Action_ChangePixel & a)
		:m_bmp(a.m_bmp),m_canBeAddedTo(a.m_canBeAddedTo),m_pixels(a.m_pixels){}

	/** makes an exact copy of this action (using new) */
	Action * clone()const{return new Action_ChangePixel(*this);}

	/** make a new ChangePixel action over an area */
	Action_ChangePixel(BitMaP * a_bmp, const SRect & a_area, const int & a_newValue)
		:m_bmp(a_bmp),m_canBeAddedTo(true)
	{
		SPoint p(a_area.getMin());
		int dim;
		do{
			addAction_ChangePixel(p, a_newValue);
			dim = 0;
		}while(p.iterate(dim, a_area));
	}

	/** ChangePixel action on specific colored pixels over an area */
	Action_ChangePixel(BitMaP * a_bmp, const SRect & a_area, const int & a_oldValue, const int & a_newValue)
		:m_bmp(a_bmp),m_canBeAddedTo(true)
	{
		SPoint p(a_area.getMin());
		int dim;
		do{
			if(a_bmp->getPixelAt(p) == a_oldValue)
				addAction_ChangePixel(p, a_newValue);
			dim = 0;
		}while(p.iterate(dim, a_area));
	}

	/** can combine with another similar action into this one event */
	virtual bool isCombinable(const Action & a_action)const
	{
		Action_ChangePixel * a = (Action_ChangePixel*)&a_action;
		return a_action.getName() == GETNAME()
			&& a->m_bmp == m_bmp
			&& m_canBeAddedTo;
	}
	/** @return true if the given action's content was added to this action */
	virtual bool combine(const Action & a_action)
	{
		Action_ChangePixel * a = (Action_ChangePixel*)&a_action;
		if(a->m_bmp == m_bmp)
		{
			TemplateVector<PixelChanged> * p = &a->m_pixels;
			for(int i = 0; i < p->size(); ++i)
			{
				//printf("adding[%d] (%d,%d) %d\n", 
				//	m_pixels.size(), p->get(i).m_pos.getX(), p->get(i).m_pos.getY(), p->get(i).m_newValue);
				addAction_ChangePixel(p->get(i).m_pos, p->get(i).m_newValue); 
			}
			return true;
		}
		return false;
	}

	bool redo()const
	{
		for(int i = 0; i < m_pixels.size(); ++i)
			m_pixels.get(i).redo(m_bmp);
		return true;
	}
	bool undo()const
	{
		for(int i = 0; i < m_pixels.size(); ++i)
			m_pixels.get(i).undo(m_bmp);
		return true;
	}

	bool isActualChange()const
	{
		bool change = false;
		for(int i = 0; !change && i < m_pixels.size(); ++i)
			change |= m_pixels.get(i).isChange();
		return true;
	}
};